/*
   Copyright (C) <2009>

   <Rishi B Agrawal> <Vineet Agarwal> <Rohit Sharma>
   <rishi.b.agrawal@gmail.com> <checkout.vineet@gmail.com>
   <imreckless@gmail.com>

   This program is free software: you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation, either version 3 of the License, or (at your
   option) any later version->

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE->  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/types.h>
#include "../include/ohsm.h"
#include "ohsm_u.h"

extern int ohsm_fd;

/****************************************************************
 *                                                              *
 *  ohsm_check_age() checks the access age for the inode        *
 *  time - inode access time                                    *
 *  check_val - policy value                                    *
 *  rel_op - relational operator                                *
 *                                                              *
 ***************************************************************/

int
ohsm_check_age(struct timespec time, long long check_val,
		unsigned int rel_op)
{

	unsigned int days;
	time_t curtime;
	struct timeval now;
	unsigned long diff_sec;
	gettimeofday(&now, NULL);

	diff_sec = now.tv_sec - time.tv_sec;

	days = diff_sec / (24 * 60 * 60);

	ohsm_debug("Days = %u , Policy value (check_val) = %lld.\n",
							days, check_val);

	switch (rel_op) {
	case LT:
		return (days < check_val);
	case GT:
		return (days > check_val);
	case EQ:
		return (days == check_val);
	case NE:
		return (days != check_val);
	case LE:
		return (days <= check_val);
	case GE:
		return (days >= check_val);
	default:
	  ohsm_debug("Unknown op type = %u\n", rel_op);
	}
	return -1;
}

/****************************************************************
 *                                                              *
 *  ohsm_check_param() checks the access age for the inode      *
 *  input - inode size in KB                                    *
 *  check_val - policy value                                    *
 *  rel_op - relational operator                                *
 *                                                              *
 ***************************************************************/

int
ohsm_check_param(long long input, long long check_val,
		unsigned char rel_op)
{
	switch (rel_op) {
	case LT:
		return (input < check_val);
	case GT:
		return (input > check_val);
	case EQ:
		return (input == check_val);
	case NE:
		return (input != check_val);
	case LE:
		return (input <= check_val);
	case GE:
		return (input >= check_val);
	default:
	  ohsm_debug("Unknown op type = %u\n", rel_op);
	}
	ohsm_debug("%llu failed to meet the op (%u) "
			"criteria with (%llu)\n", input, rel_op, check_val);
	return -1;
}

/****************************************************************
 *                                                              *
 * ohsm_check_fiot() For Future Implementation                  *
 *                                                              *
 ***************************************************************/

int
ohsm_check_fiot(struct ohsm_inode_info *temp_inode, long long check_val,
		unsigned int rel_op)
{

	return -EOPNOTSUPP;
}

/****************************************************************
 *                                                              *
 * ohsm_check_fat() For Future Implementation                   *
 *                                                              *
 ***************************************************************/

int
ohsm_check_fat(struct ohsm_inode_info *temp_inode, long long check_val,
		unsigned int rel_op)
{
	return -EOPNOTSUPP;

}

/****************************************************************
 *                                                              *
 *  ohsm_check_qualifying_policy() sets destination             *
 *  tier id for the qualified inode                             *
 *                                                              *
 ***************************************************************/

int
ohsm_check_qualifying_policy(struct ohsm_inode_info *temp_inode,
		struct ohsm_relocation_policy *policy)
{

	unsigned int val = 1;

	ohsm_debug("Src tier of policy = %u.\n", policy->src_tier);
	ohsm_debug("Dest tier of policy = %u.\n", policy->dest_tier);
	ohsm_debug("Home tier of inode = %u.\n", temp_inode->ohsm_home_tid);


		if (policy->reloc_criteria_bitmap >= OHSM_MAX_POLICIES)
			return -1;

		if (policy->reloc_criteria_bitmap == 0) {
		  ohsm_debug("Unconditional policy found.\n");
			return policy->dest_tier; 
		}

		/* File size */
		if (policy->reloc_criteria_bitmap & FSIZE) {
		  ohsm_debug("Policy based on size being tested.\n");
			val = ohsm_check_param((temp_inode->i_size/1024),
					policy->values[0], policy->rel_ops[0]);
			if (!val)
				return -1;
		}

		/* File access time */
		if (policy->reloc_criteria_bitmap & FAA) {
		  ohsm_debug("Policy based on FAA being tested.\n");
			val = val & ohsm_check_age(temp_inode->i_atime,
					policy->values[1], policy->rel_ops[1]);
			if (!val)
				return -1;
		}

		/* File modification time */
		if (policy->reloc_criteria_bitmap & FMA) {
		  ohsm_debug("Policy based on FMA being tested.\n");
			val = val & ohsm_check_age(temp_inode->i_mtime,
					policy->values[2], policy->rel_ops[2]);
			if (!val)
				return -1;
		}

		/* File I/O temperature */
		if (policy->reloc_criteria_bitmap & FIOT) {
			val = val & ohsm_check_fiot(temp_inode,
					policy->values[3],  policy->rel_ops[3]);
			if (!val)
				return -1;
		}

		/* File access temperature */
		if (policy->reloc_criteria_bitmap & FAT) {
			val = val & ohsm_check_fat(temp_inode,
					policy->values[4], policy->rel_ops[4]);
			if (!val)
				return -1;
		}

	return 0;
}

/****************************************************************
 *                                                              *
 * ohsm_find_dest_for_rule () find the destination              *
 * tier for relocation                                          *
 *                                                              *
 ***************************************************************/

int
ohsm_find_dest_for_rule(struct ohsm_inode_info *temp_inode, struct ohsm_reloc_policy *relocpol, int rule_no) {

	int policy_no = 0;
	int ret = 0;

	for (policy_no = 0; policy_no < relocpol->cnt_policies; policy_no++) {
		ohsm_debug("Checking against policy number = %d.\n", policy_no);
		ret = ohsm_check_qualifying_policy(temp_inode,
						&relocpol->policy[rule_no]);
		if (ret > 0) { 
			ohsm_debug("Policy number %d qualified.\n", policy_no);
			return ret;
		}	
	}
	return 0;
}


/****************************************************************
 *                                                              *
 * ohsm_get_inodes_for_relocation () scans the complete         *
 * inode of the file system to get an qualifying inode          *
 * and send the qualified for relocation                        *
 *                                                              *
 ***************************************************************/

int
ohsm_get_dest_tier(struct ohsm_inode_info *inode, struct ohsm_reloc_policy *relocpol, unsigned long rule_bitmap_arg)
{
	unsigned char r_bitmap;
	int rule_no = 0, ret;
	int retval = 0;
	unsigned long nr_groups;
	unsigned long rel_inode;
	unsigned long count = 0;

	if (rule_bitmap_arg > ((1 << (relocpol->cnt_policies)) - 1)) {
		ohsm_debug("Invalid relocation rule number (%lu) passed\n",
							rule_bitmap_arg);
		return -EINVAL;
	}

	ohsm_debug("The rule bitmap = %lu.\n", rule_bitmap_arg);
	ohsm_debug("The Number of Rules = %d.\n", relocpol->cnt_policies);


	r_bitmap = rule_bitmap_arg;
	for (rule_no = 0; rule_no < relocpol->cnt_policies && r_bitmap; rule_no++) {
		ohsm_debug("Checking for rule Number = %d.\n", rule_no);
		if (r_bitmap & 1) {
			ohsm_debug("Checking inode number = %ld.\n", inode->i_ino);
			retval = ohsm_find_dest_for_rule(inode, relocpol, rule_no);
			if (retval > 0) 
				return retval;
			count++;
			ohsm_debug("Total files relocated till now = %lu.\n", count);
			break;
		}
		r_bitmap = r_bitmap >> 1;
	}
	ohsm_debug("Relocation performed successfully.\n");
	return 0;
}

int ohsm_walk_dir_relocate(char *name, struct ohsm_reloc_policy *relocpol, unsigned long rule_bitmap)
{

	int tmp, ret;  /* number of characters read from a directory entry */
	struct direct *dentry;  /* pointer to a directory entry */
  	struct ohsm_inode_info *ohsm_inode;
  	struct ohsm_debug_reloc reloc_demo;
	DIR *DirPtr;  /* pointer to the directory */
	struct stat st;
	unsigned char home_tid;
	char path[MAX_CHARS];
	unsigned char tier;
	
	DirPtr = opendir(name);
	while (1)  {
		dentry = readdir(DirPtr);
			if (dentry == 0) break;  /* reached end of directory entries */
				/* process file (other than . and ..) */
			if (strcmp(dentry->d_name,".") != 0 &&
				strcmp(dentry->d_name,"..") != 0 && 
				dentry->d_name[0] != '.') {
			    /* print file name */
					/* build full path name of the file, for stat() */
			   	path[0] = '\x0';
			   	strcat(path,name);
			   	strcat(path,"/");
			   	strcat(path, dentry->d_name);
			   	tmp = lstat(path, &st); 
				ohsm_inode = (struct ohsm_inode_info *)malloc(sizeof(struct ohsm_inode_info));
				
				if (!ohsm_inode)
					return -1;
				ohsm_inode->i_uid = st.st_uid;
				ohsm_inode->i_gid = st.st_gid;
				ohsm_inode->i_ino = st.st_ino;
				ohsm_inode->i_size = st.st_size;
				ohsm_inode->i_mode = st.st_mode;
				ret = ioctl(ohsm_fd, IOC_OHSM_GET_INO_INFO, ohsm_inode);
				if (!ret) {
				//	ohsm_inode->ohsm_home_tid = home_tid;
					tier = ohsm_get_dest_tier(ohsm_inode, relocpol, rule_bitmap);
					/*
				 	* See if the home_tier != dest_tier. If so, relocate.
				 	*/
					printf("Got the destination tier for %s with inode %u as %lu home is %u\n",path, tier,ohsm_inode->i_ino, ohsm_inode->ohsm_home_tid);	
					if (tier != ohsm_inode->ohsm_home_tid) {
						reloc_demo.inum = st.st_ino;
						reloc_demo.dest = tier;
						ret = ioctl(ohsm_fd, IOC_OHSM_DEMO_RELOC, &reloc_demo);
						if (ret)
							printf("Relocation failed for %s\n",path);
					}   

				} else {
					printf("Something screwed up while getting inode info for %s\n", path);
				}

				free(ohsm_inode);	
				if (S_ISDIR(st.st_mode)) 
						ohsm_walk_dir_relocate(path, relocpol, rule_bitmap); 
		 }
	} /* End of while */
}

